
#include "Perlin.fx"

#define vLightColorDiffuse float4(0.5,0.5,0.5,1)
#define vLightColorAmbient	float4(0.2,0.2,0.2,1)
#define vLightColorSpecular	float4(1,1,1,1)


cbuffer cbNeverChanges
{
    matrix View;

    

};

cbuffer cbChangeOnResize
{
    matrix Projection;
};

cbuffer cbChangesEveryFrame
{
    matrix World;
	float fTime;
	float3 vLightDirection;
	float3 vEyePos;
};


Texture2D  txGrass;
Texture2D  txLeaf;
Texture2D  txLeaf2;


SamplerState samLinear
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

BlendState SrcAlphaBlending
{
    BlendEnable[0] = TRUE;
    SrcBlend = SRC_ALPHA;
    DestBlend = INV_SRC_ALPHA;
    BlendOp = ADD;

    RenderTargetWriteMask[0] = 0x0F;
};

BlendState SrcAlphaBlendingNone
{
    BlendEnable[0] = FALSE;

};


struct VS_INPUT
{
    float4 Pos : POSITION;
    float3 Nor : NORMAL;
    float2 Tex : TEXCOORD;
    float3 Tangent: TANGENT;
    float3 Binormal: BINORMAL;
};



struct PS_INPUT
{
    float4 Pos : SV_POSITION;
    float4 WorldPos: POSITION;
    float3 Nor : NORMAL;
    float2 Tex : TEXCOORD;
    float3 ToEye: TEXCOORD1;
    float3 Half: TEXCOORD2;
    float3x3 TBN: TEXCOORD3;

};


//Phong shading
float3 Phong(float3 normal, float3 half, float3 light, float4 color, float4 ambient, float4 diffuse, float4 specular)
{
	float4 diffuseFactor=diffuse*saturate(dot(normal,light)); 
	float  specularFactor = pow(saturate(dot(normal,half)),25);
    return ambient+color.xyz*diffuseFactor+specularFactor*specular;
}

float3 CloudShadow(float3 worldPos)
{
	float c=saturate(PerlinNoise2D(worldPos.xz/200+float2(0, fTime/4))+0.6f); //cloud shadow value
	c=(1-c)/2;

	return float3(c,c,c);
}




//
// Vertex Shader
PS_INPUT VS( VS_INPUT input ) 
{
	PS_INPUT output=(PS_INPUT)0;

	output.WorldPos=input.Pos;

    output.Pos = mul( input.Pos, World );
    output.ToEye = (vEyePos-output.Pos);
    output.WorldPos=output.Pos;
    output.Pos = mul( output.Pos, View );
    output.Pos = mul( output.Pos, Projection );
    
    output.Nor = normalize(mul( input.Nor, World ));
    output.Half = normalize(vLightDirection + normalize(output.ToEye)); 
    output.Tex =input.Tex;
    
	float3 normal = normalize(mul(input.Nor, World));
	float3 tangent= normalize(mul(input.Tangent, World));
	float3 binormal = normalize(mul(input.Binormal, World));

	output.TBN = float3x3(tangent, binormal , normal);

    
    return output;
}



//
// Pixel Shader
//
float4 PS_TreeBranch(PS_INPUT input) : SV_Target
{
	

	float value=PerlinNoise2D(input.Tex*float2(10,200));
	
    float4 color1=float4(0.9,0.9,0.7,1);
    float4 color2=float4(0.35,0.3,0.15,1);

	
	float4	Color;
	float3 normal;
	float3 color;


	if(value<0.8)
	{

		Color=color1;
		normal=generateNormal(input.Tex*10, 0.5f);
		normal=mul(normal, input.TBN).xyz; 
		color=Phong(normal,input.Half, vLightDirection, Color, Color/2, vLightColorDiffuse, vLightColorSpecular);
	}
	else
	{
		Color=color2;
		normal=generateNormal(input.Tex*200, 0.5f);
		normal=mul(normal, input.TBN).xyz; 
		color=Phong(normal,input.Half, vLightDirection, Color, vLightColorAmbient, vLightColorDiffuse, vLightColorSpecular);
	}	
	
	Color=float4(color,Color.w);
    
    return  Color;   


}

float4 PS_TreeBranch2(PS_INPUT input) : SV_Target
{
	

	float value=fbm2D(input.Tex,float2(50,5),3);
	
    float4 color1=float4(0.5,0.45,0.3,1);
    float4 color2=float4(0.5,0.5,0.4,1);

	
	float4	Color;
	float3 normal;
	float3 color;


	if(value<0.8)
	{

		Color=color1;
		normal=generateNormal(input.Tex*float2(50,5), 0.5f);
		normal=mul(normal, input.TBN).xyz; 
		color=Phong(normal,input.Half, vLightDirection, Color, Color/2, vLightColorDiffuse, vLightColorSpecular);
	}
	else
	{
		Color=color2;
		normal=generateNormal(input.Tex*float2(50,5), 0.5f);
		normal=mul(normal, input.TBN).xyz; 
		color=Phong(normal,input.Half, vLightDirection, Color, vLightColorAmbient, vLightColorDiffuse, vLightColorSpecular);
	}	
	
	Color=float4(color,Color.w);
    
    return  Color;   


}



////////////
///Geometry shader
/////////////////


void CreateGrass(float3 center, inout TriangleStream<PS_INPUT> TriStream, float3 n, float a)
{
	PS_INPUT output=(PS_INPUT)0;
	
	float3 toEye=vEyePos-center; 
    float3 side=normalize(cross(toEye,float3(0,1,0)));
    
    float angle =sin(center.x/2+a);
    
    float3 left=center-side*2.0f;;
    float3 right=center+side*2.0f;;
    float3 up=float3(0,1,0);
   	angle=angle/4;
   	up.x=up.x*cos(angle)-up.y*sin(angle);
	up.y=up.x*sin(angle)+up.y*cos(angle);
    
    up*=4.0f;
    
    
	/*angle=angle/2;
    float3 faceNormal = n; //(n+normalize( cross(faceEdgeB, faceEdgeA) ))/2;
       	faceNormal.x=faceNormal.x*cos(angle)-faceNormal.y*sin(angle);
	faceNormal.y=faceNormal.x*sin(angle)+faceNormal.y*cos(angle);*/

 

    
		///first triangle
        output.Pos = float4(left.xyz,1);        
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        //output.Nor = faceNormal;
        output.Nor = mul( up, World );
        output.Tex = float2(0,1);
  
        TriStream.Append( output );
        
        output.Pos = float4((left+up).xyz,1);       
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        //output.Nor = faceNormal;
        output.Nor = mul( up, World );
        output.Tex =  float2(0,0);

        TriStream.Append( output );
        
        output.Pos = float4(right.xyz,1);
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        //output.Nor = faceNormal;
        output.Nor = mul( up, World );
        output.Tex =  float2(1,1);

        TriStream.Append( output );
        
        
        ///second triangle
        output.Pos = float4(right.xyz,1);        
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        //output.Nor = faceNormal;
        output.Nor = mul( up, World );
        output.Tex = float2(1,1);

        TriStream.Append( output );
        
        output.Pos = float4((left+up).xyz,1);       
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        //output.Nor = faceNormal;
        output.Nor = mul( up, World );
        output.Tex =  float2(0,0);

        TriStream.Append( output );
        
        output.Pos = float4((right+up).xyz,1);
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        //output.Nor = faceNormal;
        output.Nor = mul( up, World );
        output.Tex =  float2(1,0);

        TriStream.Append( output );
        
       
   
        
      
        
        TriStream.RestartStrip();


}





[maxvertexcount(6)]
void GS( point PS_INPUT input[1], inout TriangleStream<PS_INPUT> TriStream )
{
	 if(length(input[0].ToEye)>200.0f) 
	return;

    PS_INPUT output=(PS_INPUT)0;
    
  	CreateGrass(input[0].WorldPos,TriStream, input[0].Nor, fTime);
    
}







[maxvertexcount(6)]
void GS_Leaf( point PS_INPUT input[1], inout TriangleStream<PS_INPUT> TriStream )
{
	if(length(input[0].ToEye)>200.0f) 
		return;


    PS_INPUT output=(PS_INPUT)0;
 	

	float3 pos=input[0].WorldPos;
    

    float3 side=normalize(cross(input[0].Nor,float3(0,1,0)));
    

    
    float3 left=pos-side*0.5f;;
    float3 right=pos+side*0.5f;;
    float3 up=input[0].Nor;
   	
   	float angle=sin(fTime*4.0f+pos.y)/5.0f;
   	up.x=up.x*cos(angle)-up.y*sin(angle);
	up.y=up.x*sin(angle)+up.y*cos(angle);
    
    up*=1.0f;
    
    

		///first triangle
        output.Pos = float4(left.xyz,1);        
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        //output.Nor = faceNormal;
        output.Nor = mul( up, World );
        output.Tex = float2(0,1);
  
        TriStream.Append( output );
        
        output.Pos = float4((left+up).xyz,1);       
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        //output.Nor = faceNormal;
        output.Nor = mul( up, World );
        output.Tex =  float2(0,0);

        TriStream.Append( output );
        
        output.Pos = float4(right.xyz,1);
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        //output.Nor = faceNormal;
        output.Nor = mul( up, World );
        output.Tex =  float2(1,1);

        TriStream.Append( output );
        
        
        ///second triangle
        output.Pos = float4(right.xyz,1);        
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        //output.Nor = faceNormal;
        output.Nor = mul( up, World );
        output.Tex = float2(1,1);

        TriStream.Append( output );
        
        output.Pos = float4((left+up).xyz,1);       
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        //output.Nor = faceNormal;
        output.Nor = mul( up, World );
        output.Tex =  float2(0,0);

        TriStream.Append( output );
        
        output.Pos = float4((right+up).xyz,1);
        output.Pos = mul( output.Pos, View );
        output.Pos = mul( output.Pos, Projection );
        //output.Nor = faceNormal;
        output.Nor = mul( up, World );
        output.Tex =  float2(1,0);

        TriStream.Append( output );
        
       
   
        
      
        
        TriStream.RestartStrip();

    
    

    
}

///Pixel shaders//////////////////////////////////

float4 PS_Terrain(PS_INPUT input) : SV_Target
{


	
	float value=fbm2D(input.Tex*10,float2(1,1),3)-0.2f;
	float value2=PerlinNoise2D(input.Tex*1000);
    float4 grassColor=float4(0.3,0.6,0.2,1);
    float4 sandColor=float4(0.6,0.8,0.2,1);
    float4 stoneColor=float4(0.6,0.6,0.7,1);
	
	float4	Color;
	float3 normal;
	float3 color;


	if(value2<0.97)
	{

		Color=saturate(value * sandColor +(1-value) *grassColor);
		normal=generateNormal(input.Tex*1000, 0.5f);
		normal=mul(normal, input.TBN).xyz; 
		color=Phong(normal,input.Half, vLightDirection, Color, Color/2, vLightColorDiffuse, vLightColorSpecular);
	}
	else
	{
		Color=stoneColor;
		normal=float3(0,1,0); //generateNormal(input.Tex*500, 0.1f);
		normal=mul(normal, input.TBN).xyz; 
		color=Phong(normal,input.Half, vLightDirection, Color, vLightColorAmbient, vLightColorDiffuse, vLightColorSpecular);
	}	
	

	
		
		

    

	
    
     Color=float4(color,Color.w);
    
    return  Color;   

}

float4 PS_Sky(PS_INPUT input) : SV_Target
{


	
	float value=fbm3D(input.WorldPos/2000+float3(0,0,fTime/50), float3(10,30,10), 3)-0.5;
	value/=3;
	if(value<0)
		value=0;

    float4 Color=float4(saturate(float3(0.5, 0.65, 0.85)+float3(value,value,value)),1);

    
    return  Color;   

}

float4 PS_TreeLeaf(PS_INPUT input) : SV_Target
{


	
	float4 Color=txLeaf.Sample(samLinear, input.Tex);
	float3	normal=input.Nor;
	
	float3 color=Phong(normal,input.Half, vLightDirection, Color, Color, vLightColorDiffuse, vLightColorSpecular);
     
    Color=float4(color,Color.w);
    
    return  Color;    

}

float4 PS_TreeLeaf2(PS_INPUT input) : SV_Target
{


	
	float4 Color=txLeaf2.Sample(samLinear, input.Tex);
	float3	normal=input.Nor;
	
	float3 color=Phong(normal,input.Half, vLightDirection, Color, Color, vLightColorDiffuse, vLightColorSpecular);
     
    Color=float4(color,Color.w);
    
    return  Color;    

}

float4 PS_Plants(PS_INPUT input) : SV_Target
{
	
	float4 Color=txGrass.Sample(samLinear, input.Tex);
	float3	normal=input.Nor;
	
	float3 color=Phong(normal,input.Half, vLightDirection, Color, Color/2, vLightColorDiffuse, vLightColorSpecular);
     
    Color=float4(color,Color.w);
    
    return  Color;    

}




technique10 TreeBranch
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS_TreeBranch2() ) );

    }
}

technique10 TreeLeaf
{
    pass P0
    {
  
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( CompileShader( gs_4_0, GS_Leaf() ) );
        //SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS_TreeLeaf() ) );

       
    }
}

technique10 TreeLeaf2
{
    pass P0
    {
  
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( CompileShader( gs_4_0, GS_Leaf() ) );
        //SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS_TreeLeaf2() ) );

       
    }
}

technique10 Terrain
{



    pass P0
    {

	
		
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        
		SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS_Terrain() ) );
        
        
    }
}

technique10 Sky
{
    pass P0
    {
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( NULL );
        SetPixelShader( CompileShader( ps_4_0, PS_Sky() ) );
    
    }
}

technique10 Zlants
{
    pass P0
    {
		SetBlendState( SrcAlphaBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
        SetVertexShader( CompileShader( vs_4_0, VS() ) );
        SetGeometryShader( CompileShader( gs_4_0, GS() ) );
        SetPixelShader( CompileShader( ps_4_0, PS_Plants() ) );
    
    }
}
